#include <stdio.h>
#include "Exception.h"
#include <sstream>

using namespace std;
namespace apollo
{
	namespace core
	{

		////////////////////////////////////////////////////////////////////////////////
		Exception::Exception() throw() : Throwable(), message(), cause(NULL), stackTrace(), errorLevel(ERROR) {
		}

		////////////////////////////////////////////////////////////////////////////////
		Exception::Exception( const Exception& ex ) throw() : Throwable(), message(), cause(NULL), stackTrace() ,errorLevel(ERROR){
			*this = ex;
		}

		////////////////////////////////////////////////////////////////////////////////
		Exception::Exception( const std::exception* cause ) throw() : Throwable(), message(), cause(NULL), stackTrace(), errorLevel(ERROR) {
			this->initCause( cause );
		}

		////////////////////////////////////////////////////////////////////////////////
		Exception::Exception( const char* file, const int lineNumber,
			const QString &message, const std::exception* cause ) throw() : 
		Throwable(), message(message), cause(NULL), stackTrace(), errorLevel(ERROR) {

			// Store the cause
			this->initCause( cause );

			// Set the first mark for this exception.
			this->setMark( file, lineNumber );
		}

		Exception::Exception( const char* file, const int lineNumber,
			const QString &message,
			const QString &messageKey, const QStringList& parameters, ErrorLevel errorLevel,
			const std::exception* cause ) throw() : 
		Throwable(), cause(NULL), stackTrace(),messageKey(messageKey),parameters(parameters)
			,message(message), errorLevel(errorLevel) {

				// Store the cause
				this->initCause( cause );

				// Set the first mark for this exception.
				this->setMark( file, lineNumber );
		}

		////////////////////////////////////////////////////////////////////////////////
		Exception::~Exception() throw(){
			delete this->cause;
		}

		////////////////////////////////////////////////////////////////////////////////
		void Exception::setMessage( QString &msg ){
			this->message = msg;
		}

		QString Exception::getLocaleMessage() const
		{
			//TODO: get resource and format.
			return messageKey;
		}

		////////////////////////////////////////////////////////////////////////////////
		void Exception::setMark( const char* file, const int lineNumber ) {

			// Add this mark to the end of the stack trace.
			stackTrace.push_back( std::make_pair( QString( file ), (int)lineNumber ) );
		}

		////////////////////////////////////////////////////////////////////////////////
		Exception* Exception::clone() const {
			return new Exception( *this );
		}

		////////////////////////////////////////////////////////////////////////////////
		std::vector< std::pair< QString, int> > Exception::getStackTrace() const {
			return stackTrace;
		}

		////////////////////////////////////////////////////////////////////////////////
		void Exception::setStackTrace(
			const std::vector< std::pair< QString, int> >& trace ) {

				this->stackTrace = trace;
		}

		////////////////////////////////////////////////////////////////////////////////
		void Exception::printStackTrace() const {
			printStackTrace( std::cerr );
		}

		////////////////////////////////////////////////////////////////////////////////
		void Exception::printStackTrace( std::ostream& stream ) const {
			stream << getStackTraceString().toStdString();
		}

		////////////////////////////////////////////////////////////////////////////////
		QString Exception::getStackTraceString() const {

			QString result;
			// Write the message and each stack entry.
			result += getMessage() + "\n";
			for( unsigned int ix=0; ix<stackTrace.size(); ++ix ){
				result +=  QLatin1String("\tFILE: ") + stackTrace[ix].first 
					+ QLatin1String(", LINE: ") + QString(stackTrace[ix].second) + QLatin1String("\n");
			}

			if( cause != NULL)
			{
				result += "-------------------------------------------------";
				const Exception* ptrCause = dynamic_cast<const Exception*>( cause );
				if( ptrCause == NULL ) {
					result += cause->what();
				} else {
					result += ptrCause->getStackTraceString();
				}
			}
			// Return the string from the output stream.
			return result;
		}

		////////////////////////////////////////////////////////////////////////////////
		Exception& Exception::operator =( const Exception& ex ){
			this->message = ex.message;
			this->messageKey = ex.messageKey;
			this->parameters = ex.parameters;
			this->errorLevel = ex.errorLevel;
			this->stackTrace = ex.stackTrace;
			this->initCause( ex.cause );
			return *this;
		}

		////////////////////////////////////////////////////////////////////////////////
		void Exception::initCause( const std::exception* cause ) {

			if( cause == NULL || cause == this ) {
				return;
			}

			if( this->cause != NULL ) {
				delete this->cause;
			}

			const Exception* ptrCause = dynamic_cast<const Exception*>( cause );
			if( ptrCause == NULL ) {
				this->cause = new Exception( __FILE__, __LINE__, cause->what() );
			} else {
				this->cause = ptrCause->clone();
			}

			if( this->message == "" ) {
				this->message = cause->what();
			}
		}
	}
}